﻿namespace Proto
{
    using System;
    using System.Collections.Generic;
    using System.Threading;

    using Proto.Fields;
    using Proto.Providers;

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    public class PositionProvider
    {
        private readonly PositionDataSource m_PositionDataSource;
        private Dictionary<BookCode, NotifyList<Position>> m_ListLookup;
        private Dictionary<BookCode, List<BookSubWrap>> m_SubLookup;
        
        private class BookSubWrap
        {
            public readonly BookCode BookCode;
            public readonly WeakReference<Sub<Position>> WeakReference; //Sub<Position> Sub;
            public Sub<Position> Sub{ get
            {
                return WeakReference.IsAlive ? WeakReference.Target : null;
            }}
            public BookSubWrap(BookCode book_code, Sub<Position> sub)
            {
                BookCode = book_code;
                WeakReference = new WeakReference<Sub<Position>>(sub);
            }
        }

        public PositionProvider(PositionDataSource source)
        {
            m_PositionDataSource = source;
            m_SubLookup = new Dictionary<BookCode, List<BookSubWrap>>();
            m_ListLookup = new Dictionary<BookCode, NotifyList<Position>>();
        }

        private int req_count = 0;

        private int inner_count = 0;
        public Sub<Position> Request(BookCode book)
        {
            Sub<Position> sub = new Sub<Position>(new QuickKey(FieldID.BookCode, book),
                RunnerRegistry.APP);

            BookSubWrap wrap = new BookSubWrap( book, sub );

            req_count++;
            if (req_count % 10 == 0)
                Console.WriteLine("Req " + req_count);
            
            RunnerRegistry.INTEGRATION.Enqueue(
                delegate
                    {
                        inner_count++;
                        
                        NotifyList<Position> notify;
                        if( m_ListLookup.TryGetValue(book, out notify) == false )
                        {
                            notify = m_PositionDataSource.GetPositions(book);

                            notify.NotifyUpdatePending += HandleUpdateNotification;
                            notify.NotifyAdditionPending += HandleAdditionNoticiation;
                            notify.NotifyRemovalPending += HandleRemovalNotification;
                            
                            m_ListLookup.Add(book, notify);
                        }

                        List<BookSubWrap> list;
                        if (m_SubLookup.TryGetValue(book, out list ) == false )
                        {
                            list = new List<BookSubWrap>();
                            m_SubLookup.Add( book, list);
                        }
                        list.Add(wrap);
                        if (inner_count % 10 == 0) Console.WriteLine("Inner " + inner_count);

                        if (m_ListLookup.Count % 10 == 0)
                        {
                            Console.WriteLine("Enqueue " + m_ListLookup.Count);
                        }
                        
                    });

            return sub;
        }

        private void HandleRemovalNotification(Position removal, int index)
        {
            throw new NotImplementedException();
        }

        private void HandleAdditionNoticiation(Position addition, int index)
        {
            throw new NotImplementedException();
        }

        private void HandleUpdateNotification(Position new_state, Position old_state, int index)
        {
            List<BookSubWrap> list;
            if( m_SubLookup.TryGetValue(new_state.BookCode, out list) == false )
            {
                throw new Exception();
            }
            for( int i=0 ; i < list.Count ; i++ )
            {
                while( i < list.Count && list[i].WeakReference.IsAlive == false )
                {
                    list.RemoveAt( i );
                }
                if (i < list.Count && list[i].Sub != null)
                    list[i].Sub.EnqueueUpdate( new_state );
            }
        }
    }
}